package com.typhoon.extend.shiro.realm;

import java.util.Collection;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

import com.alibaba.fastjson.JSON;
import com.typhoon.extend.constant.ShiroConstants;
import com.typhoon.extend.constant.WebConstants;
import com.typhoon.extend.dto.AccountDto;
import com.typhoon.extend.dto.ResponseDto;
import com.typhoon.extend.dto.UserDto;
import com.typhoon.extend.dto.request.LoginReq;
import com.typhoon.extend.service.AccountService;
import com.typhoon.extend.service.UserService;
import com.typhoon.extend.shiro.GlobalVarGetter;

public class CustomRealm extends AuthorizingRealm {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(CustomRealm.class);

	@Resource
	UserService userService;
	
	@Resource
	AccountService accountService;
	
	/* 
	 * 认证完成组装权限集合
	 * (non-Javadoc)
	 * @see org.apache.shiro.realm.AuthorizingRealm#doGetAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection)
	 */
	@SuppressWarnings("unchecked")
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		// TODO Auto-generated method stub
		LOGGER.info("认证完成回调进行授权:{}",JSON.toJSONString(principals));
		
        // 获取当前登录的用户名
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        Session session = SecurityUtils.getSubject().getSession();
        authorizationInfo.setRoles((Set<String>) session.getAttribute(ShiroConstants.ROLES));
        authorizationInfo.setStringPermissions((Set<String>) session.getAttribute(ShiroConstants.PERMISSIONS));
        return authorizationInfo;
	}
	
	
	/* 
	 * 认证
	 * (non-Javadoc)
	 * @see org.apache.shiro.realm.AuthenticatingRealm#doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken)
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
		LOGGER.info("身份认证:{}",JSON.toJSONString(authcToken));
		UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
		String username = token.getUsername();
		String password = String.valueOf(token.getPassword());
		Session session = SecurityUtils.getSubject().getSession();
        // 提前获取权限信息并存放入session，防止第一次登录时没有菜单的问题
		ResponseDto<AccountDto> loginResult = this.accountService.doLogin(LoginReq.create(username, password));
		if(null == loginResult || !loginResult.isSuccess()) {
			LOGGER.warn("login failed,username:{},password:{}",username,password);
			return null;
		}
		UserDto user = this.userService.queryByAccount(loginResult.getDto().getId());
		Assert.notNull(user,"用户信息不能为空");
		session.setAttribute("user", user);
        loadAuthorizationInfo(username, null);
        return new SimpleAuthenticationInfo(username,password, null, getName());
	}
	
    private void loadAuthorizationInfo(String username, String userType) {

        Session session = SecurityUtils.getSubject().getSession();
    	UserDto user = GlobalVarGetter.getUser();
    	if(null == user) {
    		LOGGER.warn("用户名:{}在session中的信息丢失",username);
    		return;
    	}
    	Map<String,Collection> authorizationMap = this.userService.getAuthorizationInfo(user.getId());

    	session.setAttribute(ShiroConstants.ROLES, authorizationMap.get(ShiroConstants.ROLES));
    	session.setAttribute(ShiroConstants.PERMISSIONS, authorizationMap.get(ShiroConstants.PERMISSIONS));
    	session.setAttribute(ShiroConstants.SYS_MENU_LIST, authorizationMap.get(ShiroConstants.SYS_MENU_LIST));
    }
    
    private void loginErrorCount(Session session) {
        Object loginErrorCount = session.getAttribute(WebConstants.LOGIN_ERROR_COUNT);
        if (loginErrorCount == null) {
            loginErrorCount = 1;
        } else {
            loginErrorCount = Integer.parseInt(loginErrorCount.toString()) + 1;
        }
        session.setAttribute(WebConstants.LOGIN_ERROR_COUNT, loginErrorCount);
    }

}
